<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="RzEpl" id="RzEpl"><span data-lake-id="ub76fa35f" id="ub76fa35f" style="color: rgb(51, 51, 51)">典型回答</span></h1>
  <p data-lake-id="uf22248e0" id="uf22248e0"><span data-lake-id="ue38f5f06" id="ue38f5f06">作为一门面向对象开发的语言，代码复用是Java引人注意的功能之一。Java代码的复用有继承，组合以及代理三种具体的表现形式。</span></p>
  <p data-lake-id="u7992ae9a" id="u7992ae9a"><span data-lake-id="uebd0a102" id="uebd0a102">​</span><br></p>
  <p data-lake-id="uf287fc68" id="uf287fc68"><span data-lake-id="u7755f578" id="u7755f578">复用性是面向对象技术带来的很棒的潜在好处之一。如果运用的好的话可以帮助我们节省很多开发时间，提升开发效率。但是，如果被滥用那么就可能产生很多难以维护的代码。</span></p>
  <p data-lake-id="u77a60815" id="u77a60815"><br></p>
  <p data-lake-id="u959826a6" id="u959826a6" style="text-align: justify"><span data-lake-id="u9abc5e2c" id="u9abc5e2c">继承（Inheritance）是一种联结类与类的层次模型。指的是一个类（称为子类、子接口）继承另外的一个类（称为父类、父接口）的功能，并可以增加它自己的新功能的能力，继承是类与类或者接口与接口之间最常见的关系；继承是一种</span><code data-lake-id="u06f64f6a" id="u06f64f6a"><span data-lake-id="ubc187f40" id="ubc187f40">is-a</span></code><span data-lake-id="ucdab50b9" id="ucdab50b9">关系。如狗是一种动物，特斯拉是一种车</span></p>
  <p data-lake-id="u2b4b9678" id="u2b4b9678" style="text-align: justify"><img src="https://cdn.nlark.com/yuque/0/2023/jpeg/5378072/1699618236124-01fee750-fc36-4187-b1c5-2d72d17007b9.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_13%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ue825df85" id="ue825df85"><br></p>
  <p data-lake-id="ue9b21ffb" id="ue9b21ffb"><br></p>
  <p data-lake-id="u2028eaf1" id="u2028eaf1" style="text-align: justify"><span data-lake-id="uc5863819" id="uc5863819" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">组合(Composition)体现的是整体与部分、拥有的关系，即</span><span data-lake-id="u801b6a76" id="u801b6a76">has-a</span><span data-lake-id="ufd215b44" id="ufd215b44" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">的关系。如狗有一个尾巴，特斯拉有轮子</span></p>
  <p data-lake-id="u3fa68f5c" id="u3fa68f5c" style="text-align: justify"><span data-lake-id="ud11dfc4d" id="ud11dfc4d" class="lake-fontsize-12" style="color: rgb(51, 51, 51)">​</span><br></p>
  <p data-lake-id="ud0816b07" id="ud0816b07" style="text-align: justify"><img src="https://cdn.nlark.com/yuque/0/2023/jpeg/5378072/1699618236125-61427e45-5e8c-426d-a7c4-ee5d65d8340f.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_12%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h3 data-lake-id="ycSjT" id="ycSjT"><span data-lake-id="u7b3cfe4e" id="u7b3cfe4e" style="color: rgb(51, 51, 51)">组合与继承的区别和联系</span></h3>
  <p data-lake-id="u8e811675" id="u8e811675"><span data-lake-id="u7aee957e" id="u7aee957e">在继承结构中，父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种</span><strong><span data-lake-id="u2db60b7b" id="u2db60b7b">白盒式代码复用</span></strong><span data-lake-id="uaaa14cd7" id="uaaa14cd7">。（如果基类的实现发生改变，那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性；）</span></p>
  <p data-lake-id="u2e0bbec3" id="u2e0bbec3"><span data-lake-id="ua50dfd70" id="ua50dfd70">​</span><br></p>
  <p data-lake-id="u485a2fec" id="u485a2fec"><strong><span data-lake-id="u49a3f268" id="u49a3f268">继承，在写代码的时候就要指名具体继承哪个类，所以，在编译期就确定了关系</span></strong><span data-lake-id="u1bbc05cf" id="u1bbc05cf">。（从基类继承来的实现是无法在运行期动态改变的，因此降低了应用的灵活性。）</span></p>
  <p data-lake-id="u5daa985c" id="u5daa985c"><span data-lake-id="u393713be" id="u393713be">​</span><br></p>
  <p data-lake-id="u7b0a3c97" id="u7b0a3c97"><span data-lake-id="ua8de0697" id="ua8de0697">组合是通过对现有的对象进行拼装（组合）产生新的、更复杂的功能。因为在对象之间，各自的内部细节是不可见的，所以我们也说这种方式的代码复用是</span><strong><span data-lake-id="uc3fbdd61" id="uc3fbdd61">黑盒式代码复用</span></strong><span data-lake-id="u44ad025e" id="u44ad025e">。（因为组合中一般都定义一个类型，所以在编译期根本不知道具体会调用哪个实现类的方法）</span></p>
  <p data-lake-id="u022e5c69" id="u022e5c69"><span data-lake-id="ua30ef807" id="ua30ef807">​</span><br></p>
  <p data-lake-id="u4c164619" id="u4c164619"><strong><span data-lake-id="u81554ed7" id="u81554ed7">组合，在写代码的时候可以采用面向接口编程。所以，类的组合关系一般在运行期确定。</span></strong></p>
  <h3 data-lake-id="LibKy" id="LibKy"><span data-lake-id="u6c359555" id="u6c359555" style="color: rgb(51, 51, 51)">优缺点对比</span></h3>
  <table data-lake-id="eKBtA" id="eKBtA" class="lake-table" style="width: 704px">
   <colgroup>
    <col width="329">
    <col width="375">
   </colgroup>
   <tbody>
    <tr data-lake-id="uca097a91" id="uca097a91">
     <td data-lake-id="u537d09b0" id="u537d09b0">
      <p data-lake-id="u6ca15bd1" id="u6ca15bd1" style="text-align: justify"><span data-lake-id="u79022c90" id="u79022c90" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">组 合 关 系</span></p></td>
     <td data-lake-id="u6fc34a1d" id="u6fc34a1d">
      <p data-lake-id="u0b28a9db" id="u0b28a9db" style="text-align: justify"><span data-lake-id="u67e96d8f" id="u67e96d8f" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">继 承 关 系</span></p></td>
    </tr>
    <tr data-lake-id="u8799a653" id="u8799a653">
     <td data-lake-id="u26cdc7fa" id="u26cdc7fa">
      <p data-lake-id="uc0f330cb" id="uc0f330cb" style="text-align: justify"><span data-lake-id="u11f687b5" id="u11f687b5" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：不破坏封装，整体类与局部类之间松耦合，彼此相对独立</span></p></td>
     <td data-lake-id="ua5df43f9" id="ua5df43f9">
      <p data-lake-id="u8f3e9ace" id="u8f3e9ace" style="text-align: justify"><span data-lake-id="u5ed51814" id="u5ed51814" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：破坏封装，子类与父类之间紧密耦合，子类依赖于父类的实现，子类缺乏独立性</span></p></td>
    </tr>
    <tr data-lake-id="u96b9ec84" id="u96b9ec84">
     <td data-lake-id="u6f7d3dd3" id="u6f7d3dd3">
      <p data-lake-id="ue92f5e20" id="ue92f5e20" style="text-align: justify"><span data-lake-id="u3eb3a096" id="u3eb3a096" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：具有较好的可扩展性</span></p></td>
     <td data-lake-id="u5e1edb8f" id="u5e1edb8f">
      <p data-lake-id="u6c86c33b" id="u6c86c33b" style="text-align: justify"><span data-lake-id="uc77288dc" id="uc77288dc" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：支持扩展，但是往往以增加系统结构的复杂度为代价</span></p></td>
    </tr>
    <tr data-lake-id="u394d67cc" id="u394d67cc">
     <td data-lake-id="uae73888d" id="uae73888d">
      <p data-lake-id="u4f71ea11" id="u4f71ea11" style="text-align: justify"><span data-lake-id="uce616290" id="uce616290" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：支持动态组合。在运行时，整体对象可以选择不同类型的局部对象</span></p></td>
     <td data-lake-id="uc487cbf1" id="uc487cbf1">
      <p data-lake-id="uee4d4c9c" id="uee4d4c9c" style="text-align: justify"><span data-lake-id="u39050765" id="u39050765" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：不支持动态继承。在运行时，子类无法选择不同的父类</span></p></td>
    </tr>
    <tr data-lake-id="ufd5096e3" id="ufd5096e3">
     <td data-lake-id="u0c750382" id="u0c750382">
      <p data-lake-id="u2d30177e" id="u2d30177e" style="text-align: justify"><span data-lake-id="u0646432a" id="u0646432a" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：整体类可以对局部类进行包装，封装局部类的接口，提供新的接口</span></p></td>
     <td data-lake-id="u57c66929" id="u57c66929">
      <p data-lake-id="u0b586a2d" id="u0b586a2d" style="text-align: justify"><span data-lake-id="u193fb68e" id="u193fb68e" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：子类不能改变父类的接口</span></p></td>
    </tr>
    <tr data-lake-id="ue9f95303" id="ue9f95303">
     <td data-lake-id="u5d40f992" id="u5d40f992">
      <p data-lake-id="ue9cb6bea" id="ue9cb6bea" style="text-align: justify"><span data-lake-id="u70ac8de0" id="u70ac8de0" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：整体类不能自动获得和局部类同样的接口</span></p></td>
     <td data-lake-id="u078b45c7" id="u078b45c7">
      <p data-lake-id="uec0b8e4d" id="uec0b8e4d" style="text-align: justify"><span data-lake-id="u3f655b25" id="u3f655b25" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：子类能自动继承父类的接口</span></p></td>
    </tr>
    <tr data-lake-id="u61b2a969" id="u61b2a969">
     <td data-lake-id="u2bbabfe7" id="u2bbabfe7">
      <p data-lake-id="u3487e111" id="u3487e111" style="text-align: justify"><span data-lake-id="u58b01813" id="u58b01813" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">缺点：创建整体类的对象时，需要创建所有局部类的对象</span></p></td>
     <td data-lake-id="ufc301a9b" id="ufc301a9b">
      <p data-lake-id="u194b1b7d" id="u194b1b7d" style="text-align: justify"><span data-lake-id="u77f79f99" id="u77f79f99" class="lake-fontsize-11" style="color: rgb(51, 51, 51)">优点：创建子类的对象时，无须创建父类的对象</span></p></td>
    </tr>
   </tbody>
  </table>
  <h3 data-lake-id="OHmyJ" id="OHmyJ"><span data-lake-id="u530db17d" id="u530db17d" style="color: rgb(51, 51, 51)">如何选择</span></h3>
  <p data-lake-id="uf1dac87e" id="uf1dac87e"><span data-lake-id="ua8066da4" id="ua8066da4">相信很多人都知道面向对象中有一个比较重要的原则『多用组合、少用继承』或者说『组合优于继承』。从前面的介绍已经优缺点对比中也可以看出，组合确实比继承更加灵活，也更有助于代码维护。</span></p>
  <p data-lake-id="ua23c116b" id="ua23c116b"><span data-lake-id="u34876b29" id="u34876b29">​</span><br></p>
  <p data-lake-id="u998eaed7" id="u998eaed7"><strong><span data-lake-id="u12e27fca" id="u12e27fca">所以，建议在同样可行的情况下，优先使用组合而不是继承。因为组合更安全，更简单，更灵活，更高效。</span></strong></p>
  <p data-lake-id="u245ce193" id="u245ce193"><strong><span data-lake-id="u1c4e552c" id="u1c4e552c">​</span></strong><br></p>
  <p data-lake-id="ucc67e844" id="ucc67e844"><span data-lake-id="u0c43bafb" id="u0c43bafb">注意，并不是说继承就一点用都没有了，前面说的是【在同样可行的情况下】。有一些场景还是需要使用继承的，或者是更适合使用继承。</span></p>
  <p data-lake-id="u7aee5ac1" id="u7aee5ac1"><span data-lake-id="u73adff14" id="u73adff14">​</span><br></p>
  <blockquote data-lake-id="u61f730ab" id="u61f730ab">
   <p data-lake-id="u7a04dacd" id="u7a04dacd"><span data-lake-id="u9622ab24" id="u9622ab24">继承要慎用，其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是，问一问自己是否需要从新类向基类进行向上转型。如果是必须的，则继承是必要的。反之则应该好好考虑是否需要继承。《Java编程思想》</span></p>
  </blockquote>
  <p data-lake-id="u433a47da" id="u433a47da"><span data-lake-id="u08726465" id="u08726465">​</span><br></p>
  <blockquote data-lake-id="u2a1d2be4" id="u2a1d2be4">
   <p data-lake-id="u47553f52" id="u47553f52"><span data-lake-id="u8bc36b80" id="u8bc36b80">只有当子类真正是超类的子类型时，才适合用继承。换句话说，对于两个类A和B，只有当两者之间确实存在is-a关系的时候，类B才应该继续类A。《Effective Java》</span></p>
  </blockquote>
 </body>
</html>